package com.zhang.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * 日期工具类
 * 基于java.util.Date、java.text.SimpleDateFormat
 *
 * @author zhangshuaiyin
 */
public class DateUtils {
    private DateUtils() {
    }

    private static final Logger logger = LoggerFactory.getLogger(DateUtils.class);
    public static final String PATTERN_MS = "yyyy-MM-dd HH:mm:ss.SSS";
    public static final String PATTERN_SEC = "yyyy-MM-dd HH:mm:ss";
    public static final String PATTERN_DATE = "yyyy-MM-dd";
    public static final String PATTERN_MS_NONE = "yyyyMMddHHmmssSSS";
    public static final String PATTERN_SEC_NONE = "yyyyMMddHHmmss";
    public static final String PATTERN_DATE_NONE = "yyyyMMdd";

    public static final String PATTERN_TIME_MS = "HH:mm:ss.SSS";
    public static final String PATTERN_TIME_SEC = "HH:mm:ss";
    public static final String PATTERN_TIME_MS_NONE = "HHmmssSSS";
    public static final String PATTERN_TIME_SEC_NONE = "HHmmss";


    /**
     * 将Date类型转换为字符串
     *
     * @param date 日期类型
     * @return 日期字符串
     */
    public static String format(Date date) {
        return format(date, PATTERN_SEC);
    }

    public static String formatDay(Date date) {
        return format(date, PATTERN_DATE);
    }

    public static String formatDayOfNone(Date date) {
        return format(date, PATTERN_DATE_NONE);
    }

    /**
     * 将Date类型转换为字符串
     *
     * @param date    日期类型
     * @param pattern 字符串格式
     * @return 日期字符串
     */
    public static String format(Date date, String pattern) {
        if (date == null) {
            return "";
        }
        if (StringUtils.isBlank(pattern)) {
            pattern = PATTERN_SEC;
        }
        return new SimpleDateFormat(pattern).format(date);
    }

    /**
     * 将字符串转换为Date类型
     *
     * @param date    字符串类型
     * @param pattern 格式
     * @return 日期类型
     */
    public static Date format(String date, String pattern) {
        if (StringUtils.isBlank(pattern)) {
            pattern = PATTERN_SEC;
        }
        Date d = null;
        if (StringUtils.isNotBlank(date)) {
            try {
                d = new SimpleDateFormat(pattern).parse(date);
            } catch (ParseException pe) {
                pe.printStackTrace();
            }
        }
        return d;
    }

    /**
     * 将字符串转换为Date类型
     *
     * @param date 字符串类型
     * @return 日期类型
     */
    public static Date format(String date) {
        return format(date, PATTERN_SEC);
    }

    public static Date formatDay(String date) {
        return format(date, PATTERN_DATE);
    }

    public static Date formatDayOfNone(String date) {
        return format(date, PATTERN_DATE_NONE);
    }

    public static Date format2(Date date, String pattern) {
        if (StringUtils.isBlank(pattern)) {
            pattern = PATTERN_SEC;
        }
        String d = format(date, pattern);
        return format(d, pattern);
    }

    /**
     * Description:加一天
     *
     * @param date
     * @return
     */
    public static Date addDay(Date date) {
        Calendar calendar = toCalendar(date);
        calendar.add(Calendar.DAY_OF_YEAR, 1);
        return calendar.getTime();
    }

    /**
     * Description:判断一个日期是否为工作日(非周六周日)
     *
     * @param date
     * @return
     */
    public static boolean isWorkDay(Date date) {
        Calendar calendar = toCalendar(date);
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        return dayOfWeek != Calendar.SUNDAY && dayOfWeek != Calendar.SATURDAY;
    }

    /**
     * 求开始截至日期之间的天数差.准确
     *
     * @param begin 开始日期
     * @param end   截至日期
     * @return 返回相差天数
     */
    public static int getDays(String begin, String end) {
        SimpleDateFormat format = new SimpleDateFormat(PATTERN_SEC);

        Date d1 = null;
        Date d2 = null;
        int days = 0;
        try {
            d1 = format.parse(begin);
            d2 = format.parse(end);

            // 毫秒ms
            long diff = d2.getTime() - d1.getTime();

            // long diffSeconds = diff / 1000 % 60;
            // long diffMinutes = diff / (60 * 1000) % 60;
            // long diffHours = diff / (60 * 60 * 1000) % 24;
            long diffDays = diff / (24 * 60 * 60 * 1000);
            days = (int) diffDays;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return days;
    }

    public static String getDayOfWeek(Date date) {
        Calendar calendar = toCalendar(date);
        return "周" + toWeekChNumber(calendar.get(Calendar.DAY_OF_WEEK));
    }

    /**
     * 数字转为中文。 "1234567"->"日一二三四五六"
     *
     * @param num 长度为1
     * @return
     */
    private static String toWeekChNumber(int num) {
        final String str = "日一二三四五六";
        return str.substring(num, num + 1);
    }

    /**
     * Description:指定日期加或减分钟
     *
     * @param date    日期
     * @param minutes 分钟数
     * @return
     */
    public static Date addMinutes(Date date, int minutes) {
        Calendar calendar = toCalendar(date);
        calendar.add(Calendar.MINUTE, minutes);
        return calendar.getTime();
    }

    /**
     * Description:指定日期加或减days天
     *
     * @param date 日期
     * @param days 天数
     * @return
     */
    public static Date addDays(Date date, int days) {
        Calendar calendar = toCalendar(date);
        calendar.add(Calendar.DAY_OF_YEAR, days);
        return calendar.getTime();
    }

    /**
     * Description:指定日期加或减months月
     *
     * @param date
     * @param months
     * @return
     */
    public static Date addMonths(Date date, int months) {
        Calendar calendar = toCalendar(date);
        calendar.add(Calendar.MONTH, months);
        return calendar.getTime();
    }

    /**
     * Description:指定日期加或减years年
     *
     * @param date
     * @param years
     * @return
     */
    public static Date addYears(Date date, int years) {
        Calendar calendar = toCalendar(date);
        calendar.add(Calendar.YEAR, years);
        return calendar.getTime();
    }

    /**
     * 将java.util.Date类型转换成java.util.Calendar类型
     *
     * @param date
     * @return
     */
    public static Calendar toCalendar(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar;
    }

    /**
     * 指定日期所在月的第一天
     *
     * @param date
     * @return
     */
    public static Date getFirstDayOfMonth(Date date) {
        Calendar calendar = toCalendar(date);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        return calendar.getTime();
    }

    /**
     * Description:取一个月的最后一天
     *
     * @param date
     * @return
     */
    public static Date getLastDayOfMonth(Date date) {
        Calendar calendar = toCalendar(date);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.add(Calendar.MONTH, 1);
        calendar.add(Calendar.DAY_OF_YEAR, -1);
        return calendar.getTime();
    }

    /**
     * 指定日期所在上月的第一天
     *
     * @param date
     * @return
     */
    public static Date getFirstDayOfPreMonth(Date date) {
        Calendar calendar = toCalendar(date);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.add(Calendar.MONTH, -1);
        return calendar.getTime();
    }

    /**
     * 指定日期所在年的第一天
     *
     * @param date
     * @return
     */
    public static Date getFirstDayOfYear(Date date) {
        Calendar calendar = toCalendar(date);
        calendar.set(Calendar.DAY_OF_YEAR, 1);
        return calendar.getTime();
    }

    /**
     * 取一年的最后一天
     *
     * @param date
     * @return
     */
    public static Date getLastDayOfYear(Date date) {
        Calendar calendar = toCalendar(date);
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        calendar.set(Calendar.MONTH, 0);
        calendar.add(Calendar.YEAR, 1);
        calendar.add(Calendar.DAY_OF_YEAR, -1);
        return calendar.getTime();
    }

    /**
     * 日期合法性检查
     *
     * @param s       日期字符串
     * @param pattern 格式
     * @return
     */
    public static boolean isValidDate(String s, String pattern) {
        if (StringUtils.isBlank(s)) {
            return Boolean.FALSE;
        }
        try {
            Date date = format(s, pattern);
            String tmp = format(date, pattern);
            return s.equals(tmp);
        } catch (Exception e) {
            logger.info("日期合法性检查, 异常:{}", e.getLocalizedMessage());
            return Boolean.FALSE;
        }
    }

    /**
     * 设置时间为00:00:00.000
     *
     * @param date
     * @return
     */
    public static Date toStartDay(Date date) {
        Calendar calendar = toCalendar(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 设置时间为00:00:00.000
     *
     * @param date
     * @return
     */
    public static Date toEndDay(Date date) {
        Calendar calendar = toCalendar(date);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return calendar.getTime();
    }

    /**
     * 获取当前日期
     *
     * @return
     */
    public static Date now() {
        return new Date();
    }

    /**
     * 获取当前日期字符串, 默认格式: yyyy-MM-dd HH:mm:ss
     *
     * @return
     */
    public static String nowString() {
        return format(now());
    }

    /**
     * 获取当前日期字符串
     *
     * @return
     */
    public static String nowString(String pattern) {
        return format(now(), pattern);
    }

    /**
     * 获取当前日期字符串, 格式默认: yyyyMMdd
     *
     * @return
     */
    public static String nowDateString() {
        return format(now(), PATTERN_DATE_NONE);
    }

    /**
     * 获取当前时间字符串, 格式默认: HHmmssSSS
     *
     * @return
     */
    public static String nowTimeString() {
        return format(now(), PATTERN_TIME_MS_NONE);
    }

    /**
     * 根据日期长度识别日期格式, 不匹配返回null
     *
     * @param dateString
     * @return
     */
    public static String scanPattern(String dateString) {
        if (dateString != null) {
            switch (dateString.length()) {
                case 23:
                    //yyyy-MM-dd HH:mm:ss.SSS
                    return PATTERN_MS;
                case 19:
                    //yyyy-MM-dd HH:mm:ss
                    return PATTERN_SEC;
                case 17:
                    //yyyyMMddHHmmssSSS
                    return PATTERN_MS_NONE;
                case 14:
                    //yyyyMMddHHmmss
                    return PATTERN_SEC_NONE;
                case 12:
                    //HH:mm:ss.SSS
                    return PATTERN_TIME_MS;
                case 10:
                    //yyyy-MM-dd
                    return PATTERN_DATE;
                case 9:
                    //HHmmssSSS
                    return PATTERN_TIME_MS_NONE;
                case 8:
                    //yyyyMMdd
                    //HH:mm:ss
                    return dateString.indexOf(":") != -1 ? PATTERN_TIME_SEC : PATTERN_DATE_NONE;
                case 6:
                    //HHmmss
                    return PATTERN_TIME_SEC_NONE;
                default:
                    logger.info("日期:{}, 暂不支持该日期格式, 请补充.", dateString);
                    return null;
            }
        }
        return null;
    }

    /**
     * @param dateString
     * @return
     */
    public static String scanPatternNone(String dateString) {
        if (dateString != null) {
            switch (dateString.length()) {
                case 17:
                    //yyyyMMddHHmmssSSS
                    return PATTERN_MS_NONE;
                case 14:
                    //yyyyMMddHHmmss
                    return PATTERN_SEC_NONE;
                case 9:
                    //HHmmssSSS
                    return PATTERN_TIME_MS_NONE;
                case 8:
                    //yyyyMMdd
                    //HH:mm:ss
                    return dateString.indexOf(":") != -1 ? null : PATTERN_DATE_NONE;
                case 6:
                    //HHmmss
                    return PATTERN_TIME_SEC_NONE;
                default:
                    logger.warn("日期:{}, 日期格式不匹配", dateString);
                    return null;
            }
        }
        return null;
    }
}
